#pragma once
#include <map>
#include <vector>

// predeclare the octree....
template<typename A, typename B> class TOctree;
struct OctreeNodeInfoCompact;
struct FSceneNodeOctreeSemantics;
typedef TOctree<OctreeNodeInfoCompact, FSceneNodeOctreeSemantics> SceneOctree;
typedef std::map<std::string, std::string> NameValeParamMap;
class UpdateObject;
class NxScene;

class Scene
{
public:
	typedef std::map<std::string, Camera*> CameraList;
	typedef std::map<std::string, MovableObject*> MovableObjectCollection;
	typedef std::map<std::string, MovableObjectCollection*> MovableObjectCollectionMap;
	typedef std::map<std::string, SceneNode*> SceneNodeList;
	typedef std::vector<UpdateObject*> UpdateObjectVector;
	typedef std::vector<Renderable*> RenderableList;

public:
	Scene(SceneManager* creator, float halfWorldSize);
	~Scene();
	
	inline SceneManager* getCreator() const { return mCreator; }
	inline RenderQueue* getRenderQueue() const { return mRenderQueue; }
	inline SceneOctree* getSceneOctree() const { return mOctree; }
	inline const RenderableList& getRenderableList() const { return mRenderableList; }
	inline NxScene* getPhysicsScene() { return mPhysicsScene; }

	// camera.
	Camera* createCamera(const std::string& name);
	Camera* getCamera(const std::string& name);
	bool hasCamera(const std::string& name) const;
	void removeCamera(const std::string& name);
	void removeAllCamera();

	// SceneNodeList
	SceneNode* createSceneNode(
		const std::string& name, 
		const Vector3& pos = Vector3::Zero,
		const Rotator& rotate = Rotator::Identity,
		const Vector3& scale = Vector3::One);
	SceneNode* getSceneNode(const std::string& name);
	bool hasSceneNode(const std::string& name) const;
	void removeSceneNode(const std::string& name);
	void removeAllSceneNode();

	// movable object collection.
	MovableObjectCollection* getMovableObjectCollection(const std::string& typeName);
	const MovableObjectCollection* getMovableObjectCollection(const std::string& typeName) const;

	// movable objects.
	template<class T>
	T* Create(const std::string& name, NameValeParamMap* params = NULL);
	bool AddMovableObject(MovableObject* object);
	MovableObject* FindMovableObject(const std::string& name, const std::string& typeName) const;
	bool Destroy(const std::string& name, const std::string& typeName);
	bool Destroy(MovableObject* object);
	void DestroyAllMovable();

	void ClearScene();
	void PendingUpdate(UpdateObject* object);
	void RemovePendingUpdate(UpdateObject* object);
	void Update(float elapsedTime);

	void CreateUpdater(UpdateObject* object);
	void RemoveUpdater(size_t ticket, UpdateObject* object);

	void AddToScene(Renderable* rend);
	void RemoveFromScene(Renderable* rend);

	// raycast.
	float RayCast(const Vector3& pos, const Vector3& dir, float maxDistance);

private:
	SceneManager* mCreator;
	RenderQueue* mRenderQueue;
	SceneOctree* mOctree;
	NxScene* mPhysicsScene;

	CameraList mCameraList;
	MovableObjectCollectionMap mMovableObjectCollectionMap;
	SceneNodeList mSceneNodeList;
	UpdateObjectVector mPendingUpdates;
	UpdateObjectVector mUpdateList;
	RenderableList mRenderableList;
};


template<class T>
inline T* Scene::Create(const std::string& name, NameValeParamMap* params) 
{ 
	T* ret = ALIGN_NEW(T, (name, this));
	if (!ret->Init(params))
	{
		ALIGN_DELETE(T, ret);
		return NULL;
	}

	AddMovableObject(ret);
	return ret;
}
